/**
 * Created by gaimeng on 15/3/9.
 */

IndoorMap3d = function (mapdiv) {
  var _this = this;
  var _theme = null;
  var _mapDiv = mapdiv,
    _canvasWidth = _mapDiv.clientWidth,
    _canvasWidthHalf = _canvasWidth / 2,
    _canvasHeight = _mapDiv.clientHeight,
    _canvasHeightHalf = _canvasHeight / 2;

  var _scene, _controls, _projector, _rayCaster;
  var _canvasDiv;
  var _selected;
  var _showNames = true, _showPubPoints = true;
  var _curFloorId = 0;
  var _curPosition = null;
  var _nonNavigationMarks = null;
  var _selectionListener = null;
  var _sceneOrtho, _cameraOrtho;//for 2d
  var _spriteMaterials = [], _pubPointSprites = null, _nameSprites = null, _curSprite = null,
    _nonNavigationSprites = null;

  this.camera = null;
  this.renderer = null;
  this.mall = null;
  this.is3d = true;

  this.init = function () {

    // perspective scene for normal 3d rendering
    _scene = new THREE.Scene();
    _this.camera = new THREE.PerspectiveCamera(System.config.zoom, _canvasWidth / _canvasHeight, 0.1, 2000);

    //orthogonal scene for sprites 2d rendering
    _sceneOrtho = new THREE.Scene();
    _cameraOrtho = new THREE.OrthographicCamera(-_canvasWidthHalf, _canvasWidthHalf, _canvasHeightHalf, -_canvasHeightHalf, 1, 1000);
    _cameraOrtho.position.z = 10;

    //controls
    _controls = new THREE.OrbitControls(_this.camera);

    //renderer
    _this.renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    _this.renderer.autoClear = false;

    // //set up the lights
    var light = new THREE.AmbientLight(0xffffff);
    light.position.set(-500, 500, -500);
    _scene.add(light);


    // var light = new THREE.PointLight(0xcccccc);
    //
    // light.position.set(500, -500, 500);
    // _scene.add(light);
    // var light = new THREE.PointLight(0xcccccc);
    //
    // light.position.set(-500, -500, -500);
    // _scene.add(light);
    //canvas div
    _this.renderer.setSize(_mapDiv.clientWidth, _mapDiv.clientHeight);
    _canvasDiv = _this.renderer.domElement;
    _mapDiv.appendChild(_canvasDiv);

    _mapDiv.style.overflow = "hidden";
    _canvasDiv.style.width = "100%";
    _canvasDiv.style.height = "100%";
  }


  this.setRotate = function (rotate) {
    // alert(rotate)
    _controls.rotate(rotate);
    return _this;
  }

  this.setRotateLeft = function (rotate) {
    // alert(rotate)
    _controls.rotateLeft(rotate);
    return _this;
  }

  this.setRotateRight = function (rotate) {
    // alert(rotate)
    _controls.rotateRight(rotate);
    return _this;
  }

  this.setTheme = function (theme) {
    if (_theme == null) {
      _theme = theme
    } else if (_theme != theme) {
      _theme = theme;
      _this.parse(_this.mall.jsonData); //parse
    }
    return _this;
  }

  this.theme = function () {
    return _theme;
  }

  //load the map by the json file name
  this.load = function (fileName, callback) {
    var loader = new IndoorMapLoader(true);
    _theme = default3dTheme;
    loader.load(fileName, function (mall) {
      _this.mall = mall;
      _scene.add(_this.mall.root);
      _scene.mall = mall;
      if (callback) {
        callback();
      }
      _this.renderer.setClearColor(_theme.background);
      if (_curFloorId == 0) {
        _this.showAllFloors();
      } else {
        _this.showFloor(_curFloorId);
      }

    });
    return _this;
  }

  //parse the json file
  this.parse = function (json) {
    if (_theme == null) {
      _theme = default3dTheme;
    }
    _this.mall = ParseModel(json, _this.is3d, _theme);
    _scene.mall = _this.mall;
    _this.showFloor(_this.mall.getDefaultFloorId());
    _this.renderer.setClearColor(_theme.background);
    _scene.add(_this.mall.root);
    _mapDiv.style.background = _theme.background;
    return _this;
  }

  //reset the camera to default configuration
  this.setDefaultView = function () {

    var camAngle = _this.mall.FrontAngle + Math.PI / 2;
    var camDir = [Math.cos(camAngle), Math.sin(camAngle)];
    var camLen = System.config.zoom * 200;
    var tiltAngle = 60 * Math.PI / 180.0;
    // _this.camera.position.set(camDir[1] * camLen, Math.sin(tiltAngle) * camLen, Math.cos(tiltAngle) * camLen);//TODO: adjust the position automatically
    _this.camera.position.set(5,5,5);
    _this.camera.lookAt(_scene.position);

    _controls.reset();
    _controls.viewChanged = true;
    return _this;
  }

  //set top view
  this.setTopView = function () {
    _this.camera.position.set(-50, 50, 0);
    return _this;
  }

  //TODO:adjust camera to fit the building
  this.adjustCamera = function () {
    _this.setDefaultView();

  }

  this.zoomIn = function (zoomScale) {
    _controls.zoomOut(zoomScale);
    redraw();
  }

  this.setNavigationPath = function (startPoi, endPoi) {
    var data = {
      buildingid: 10138,
      sx: startPoi.x,
      sy: startPoi.y,
      sfid: startPoi.floorId,
      ex: endPoi.x,
      ey: endPoi.y,
      efid: endPoi.floorId
    };
    $.get('/GetNavigationService.service', data, function (response) {
      var points = (response.gs)[0].Points;
      var material = new THREE.LineDashedMaterial({vertexColors: false, dashSize: 5, gapSize: 5, color: 0x839848});

      // var material = new THREE.LineBasicMaterial({ color: 0x0000ff });
      var geometry = new THREE.Geometry();
      for (var i = 0; i < points.length; i++) {
        geometry.vertices.push(new THREE.Vector3(parseInt(points[i].X), parseInt(points[i].Y), 0));
        var line = new THREE.Line(geometry, material);
        line.position.z = 1;
        _sceneOrtho.add(line);
      }

      redraw();

    });

  }

  this.clearNavigationPath = function () {

  }

  this.zoomOut = function (zoomScale) {
    _controls.zoomIn(zoomScale);
    redraw();
  }

  this.setUserPosition = function (position) {
    _curPosition = position;
    if (_curFloorId == _curPosition.floorId) {
      createCurPositonSprite();
    }
    redraw();
  }

  this.setNonNavigationMarks = function (marks) {
    _nonNavigationMarks = marks;
    createNoNavigationMarksSprite();
    redraw();
  }

  this.clearNonNavigationMarks = function () {

    createNoNavigationMarksSprite();
    _nonNavigationMarks = null;
    redraw();
  }


  //show floor by id
  this.showFloor = function (floorid) {
    _curFloorId = floorid;
    if (_scene.mall == null) {
      return;
    }
    _scene.mall.showFloor(floorid);
    _this.adjustCamera();
    if (_showPubPoints) {
      createPubPointSprites(floorid);
    }
    if (_showNames) {
      createNameSprites(floorid);
    }
    createCurPositonSprite();
    createNoNavigationMarksSprite();
    redraw();
    return _this;
  }

  //show all floors
  this.showAllFloors = function () {
    _curFloorId = 0; //0 for showing all
    if (_this.mall == null) {
      return;
    }
    _this.mall.showAllFloors();
    _this.adjustCamera();
    clearPubPointSprites();
    clearNameSprites();
    clearCurPositionSprites();
    return _this;
  }

  //set if the objects are selectable
  this.setSelectable = function (selectable) {
    if (selectable) {
      _projector = new THREE.Projector();
      _rayCaster = new THREE.Raycaster();
      _mapDiv.addEventListener('mousedown', onSelectObject, false);
      _mapDiv.addEventListener('touchstart', onSelectObject, false);
    } else {
      _mapDiv.removeEventListener('mousedown', onSelectObject, false);
      _mapDiv.removeEventListener('touchstart', onSelectObject, false);
    }
    return _this;
  }

  //set if the user can pan the camera
  this.setMovable = function (movable) {
    _controls.enable = movable;
    return _this;
  }

  //show the labels
  this.showAreaNames = function (show) {
    _showNames = show == undefined ? true : show;
    return _this;
  }

  //show pubPoints(entries, ATM, escalator...)
  this.showPubPoints = function (show) {
    _showPubPoints = show == undefined ? true : show;
    return _this;
  }

  //get the selected object
  this.getSelectedId = function () {
    return _selected.id;
  }

  //the callback function when sth is selected
  this.setSelectionListener = function (callback) {
    _selectionListener = callback;
    return _this;
  }

  //select object by id
  this.selectById = function (id) {
    var floor = _this.mall.getCurFloor();
    for (var i = 0; i < floor.children.length; i++) {
      if (floor.children[i].id && floor.children[i].id == id) {
        if (_selected) {
          _selected.material.color.setHex(_selected.currentHex);
        }
        select(floor.children[i]);
      }
    }
  }


  //select object(just hight light it)
  function select(obj) {
    // obj.currentHex = _selected.material.color.getHex();
    obj.material.color = new THREE.Color(_theme.selected);
    obj.scale = new THREE.Vector3(2, 2, 2);
  }

  function onSelectObject(event) {

    // find intersections
    event.preventDefault();
    var mouse = new THREE.Vector2();
    if (event.type == "touchstart") {
      mouse.x = (event.touches[0].clientX / _canvasDiv.clientWidth) * 2 - 1;
      mouse.y = -(event.touches[0].clientY / _canvasDiv.clientHeight) * 2 + 1;
    } else {
      mouse.x = (event.clientX / _canvasDiv.clientWidth) * 2 - 1;
      mouse.y = -(event.clientY / _canvasDiv.clientHeight) * 2 + 1;
    }
    var vector = new THREE.Vector3(mouse.x, mouse.y, 1);
    vector.unproject(_this.camera);

    _rayCaster.set(_this.camera.position, vector.sub(_this.camera.position).normalize());

    var intersects = _rayCaster.intersectObjects(_this.mall.root.children[0].children);

    if (intersects.length > 0) {

      if (_selected != intersects[0].object) {

        if (_selected) {
          // _selected.material.color.setHex(_selected.currentHex);
        }
        for (var i = 0; i < intersects.length; i++) {
          _selected = intersects[i].object;
          if (_selected.type && _selected.type == "solidroom") {
            select(_selected);
            console.log(_selected);
            if (_selectionListener) {

              _selectionListener(_selected.id); //notify the listener
            }
            break;
          } else {
            _selected = null;
          }
          if (_selected == null && _selectionListener != null) {
            _selectionListener(-1);
          }
        }
      }

    } else {

      if (_selected) {
        _selected.material.color.setHex(_selected.currentHex);
      }

      _selected = null;
      if (_selectionListener) {
        _selectionListener(-1); //notify the listener
      }
    }
    redraw();

  }

  function redraw() {
    _controls.viewChanged = true;
  }

  function animate() {
    requestAnimationFrame(animate);
    _controls.update();
    if (_controls.viewChanged) {

      _this.renderer.clear();
      _this.renderer.render(_scene, _this.camera);

      if (_showNames || _showPubPoints) {
        updateLabels();
      }
      _this.renderer.clearDepth();
      _this.renderer.render(_sceneOrtho, _cameraOrtho);

    }

    _controls.viewChanged = false;
  }

  //load Sprites
  function loadSprites() {
    if (_this.mall != null && _spriteMaterials.length == 0) {
      var images = _theme.pubPointImg;
      for (var key in images) {
        var texture = THREE.ImageUtils.loadTexture(images[key], undefined, redraw);
        var material = new THREE.SpriteMaterial({map: texture});
        _spriteMaterials[key] = material;
      }
    }
    _spriteMaterials.isLoaded = true;
  }

  //labels includes pubPoints and shop names
  function updateLabels() {
    var mall = _this.mall;
    if (mall == null || _controls == null || !_controls.viewChanged) {
      return;
    }
    var curFloor = mall.getCurFloor();
    if (curFloor == null) {
      return;
    }

    var projectMatrix = null;

    if (_showNames) {
      if (_nameSprites != undefined) {
        projectMatrix = new THREE.Matrix4();
        projectMatrix.multiplyMatrices(_this.camera.projectionMatrix, _this.camera.matrixWorldInverse);

        updateSprites(_nameSprites, projectMatrix);
      }

    }

    if (_showPubPoints) {
      if (_pubPointSprites != undefined) {
        if (!projectMatrix) {
          projectMatrix = new THREE.Matrix4();
          projectMatrix.multiplyMatrices(_this.camera.projectionMatrix, _this.camera.matrixWorldInverse);
        }
        updateSprites(_pubPointSprites, projectMatrix);
      }
    }

    if (_curPosition && _curPosition.floorId == _curFloorId && _curSprite != undefined) {
      if (!projectMatrix) {
        projectMatrix = new THREE.Matrix4();
        projectMatrix.multiplyMatrices(_this.camera.projectionMatrix, _this.camera.matrixWorldInverse);
      }
      updateSprites(_curSprite, projectMatrix);
    }

    if (_nonNavigationMarks && _nonNavigationSprites != undefined) {
      if (!projectMatrix) {
        projectMatrix = new THREE.Matrix4();
        projectMatrix.multiplyMatrices(_this.camera.projectionMatrix, _this.camera.matrixWorldInverse);
      }
      updateSprites(_nonNavigationSprites, projectMatrix);
    }

    _controls.viewChanged = false;
  };

  //update sprites
  function updateSprites(spritelist, projectMatrix) {
    for (var i = 0; i < spritelist.children.length; i++) {
      var sprite = spritelist.children[i];
      var vec = new THREE.Vector3(sprite.oriX * 0.1, 0, -sprite.oriY * 0.1);
      vec.applyMatrix4(projectMatrix);

      var x = Math.round(vec.x * _canvasWidthHalf);
      var y = Math.round(vec.y * _canvasHeightHalf);
      sprite.position.set(x, y, 1);

      //check collision with the former sprites
      var visible = true;
      var visibleMargin = 5;
      for (var j = 0; j < i; j++) {
        var img = sprite.material.map.image;
        if (!img) { //if img is undefined (the img has not loaded)
          visible = false;
          break;
        }

        var imgWidthHalf1 = sprite.width / 2;
        var imgHeightHalf1 = sprite.height / 2;
        var rect1 = new Rect(sprite.position.x - imgWidthHalf1, sprite.position.y - imgHeightHalf1,
          sprite.position.x + imgHeightHalf1, sprite.position.y + imgHeightHalf1);

        var sprite2 = spritelist.children[j];
        var sprite2Pos = sprite2.position;
        var imgWidthHalf2 = sprite2.width / 2;
        var imgHeightHalf2 = sprite2.height / 2;
        var rect2 = new Rect(sprite2Pos.x - imgWidthHalf2, sprite2Pos.y - imgHeightHalf2,
          sprite2Pos.x + imgHeightHalf2, sprite2Pos.y + imgHeightHalf2);

        if (sprite2.visible && rect1.isCollide(rect2)) {
          visible = false;
          break;
        }

        rect1.tl[0] -= visibleMargin;
        rect1.tl[1] -= visibleMargin;
        rect2.tl[0] -= visibleMargin;
        rect2.tl[1] -= visibleMargin;


        if (sprite.visible == false && rect1.isCollide(rect2)) {
          visible = false;
          break;
        }
      }
      sprite.visible = visible;
    }
  }

  function createCurPositonSprite() {
    if (!_curSprite) {

      _curSprite = new THREE.Object3D();
    } else {
      clearCurPositionSprites();
    }
    if (_curPosition && _curPosition.floorId == _curFloorId) {
      var imgWidth = 32, imgHeight = 32;
      var sprite = new THREE.Sprite(_spriteMaterials['101']);

      sprite.scale.set(imgWidth, imgHeight, 1);

      sprite.oriX = _curPosition.x;
      sprite.oriY = -_curPosition.y;
      sprite.width = imgWidth;
      sprite.height = imgHeight;
      _curSprite.add(sprite);
    }
    _sceneOrtho.add(_curSprite);

  }

  function createNoNavigationMarksSprite() {
    if (!_nonNavigationSprites) {

      _nonNavigationSprites = new THREE.Object3D();
    } else {
      clearNoNavigationMarkSprites();
    }
    if (_nonNavigationMarks) {
      for (var i = 0; i < _nonNavigationMarks.length; i++) {
        if (_nonNavigationMarks[i].floorId == _curFloorId) {
          var imgWidth = 32, imgHeight = 32;
          var sprite = new THREE.Sprite(_spriteMaterials['102']);
          sprite.scale.set(imgWidth, imgHeight, 1);
          sprite.oriX = _nonNavigationMarks[i].x;
          sprite.oriY = -_nonNavigationMarks[i].y;
          sprite.width = imgWidth;
          sprite.height = imgHeight;
          _nonNavigationSprites.add(sprite);
        }
      }
    }
    _sceneOrtho.add(_nonNavigationSprites);

  }

  //create the pubpoint sprites in a floor by the floor id
  function createPubPointSprites(floorId) {
    if (!_spriteMaterials.isLoaded) {
      loadSprites();
    }

    if (!_pubPointSprites) {

      _pubPointSprites = new THREE.Object3D();
    } else {
      clearPubPointSprites();
    }

    var pubPointsJson = _this.mall.getFloorJson(_this.mall.getCurFloorId()).PubPoint;
    var imgWidth, imgHeight;
    for (var i = 0; i < pubPointsJson.length; i++) {
      var spriteMat = _spriteMaterials[pubPointsJson[i].Type];
      if (spriteMat !== undefined) {
        imgWidth = 32, imgHeight = 32;
        var sprite = new THREE.Sprite(spriteMat);
        sprite.scale.set(imgWidth, imgHeight, 1);
        sprite.oriX = pubPointsJson[i].Outline[0][0][0];
        sprite.oriY = pubPointsJson[i].Outline[0][0][1];
        sprite.width = imgWidth;
        sprite.height = imgHeight;
        _pubPointSprites.add(sprite);
      }
    }
    _sceneOrtho.add(_pubPointSprites);
  }

  //creat the funcArea Name sprites of a floor
  function createNameSprites(floorId) {
    if (!_nameSprites) {
      _nameSprites = new THREE.Object3D();
    } else {
      clearNameSprites();
    }
    var funcAreaJson = _this.mall.getFloorJson(_this.mall.getCurFloorId()).FuncAreas;
    for (var i = 0; i < funcAreaJson.length; i++) {
      var sprite = makeTextSprite(funcAreaJson[i].Name, _theme.fontStyle);
      // var sprite = makeTextSprite(funcAreaJson[i].Name_en, _theme.fontStyle);
      sprite.oriX = funcAreaJson[i].Center[0];
      sprite.oriY = funcAreaJson[i].Center[1];
      _nameSprites.add(sprite);
    }
    _sceneOrtho.add(_nameSprites);
  }


  function clearNameSprites() {
    if (_nameSprites == null) {
      return;
    }
    _nameSprites.remove(_nameSprites.children);
    _nameSprites.children.length = 0;
  }

  function clearPubPointSprites() {
    if (_pubPointSprites == null) {
      return;
    }
    _pubPointSprites.remove(_pubPointSprites.children);
    _pubPointSprites.children.length = 0;
  }

  function clearCurPositionSprites() {
    if (_curSprite == null) {
      return;
    }
    _curSprite.remove(_curSprite.children);
    _curSprite.children.length = 0;
  }


  function clearNoNavigationMarkSprites() {
    if (_nonNavigationSprites == null) {
      return;
    }
    _nonNavigationSprites.remove(_nonNavigationSprites.children);
    _nonNavigationSprites.children.length = 0;
  }


  function makeTextSprite(message, parameters) {

    if (parameters === undefined) parameters = {};

    var fontface = parameters.hasOwnProperty("fontface") ?
      parameters["fontface"] : "Arial";

    var fontsize = parameters.hasOwnProperty("fontsize") ?
      parameters["fontsize"] : 18;

    var borderThickness = parameters.hasOwnProperty("borderThickness") ?
      parameters["borderThickness"] : 2;

    var borderColor = parameters.hasOwnProperty("borderColor") ?
      parameters["borderColor"] : {r: 0, g: 0, b: 0, a: 1.0};

    var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
      parameters["backgroundColor"] : {r: 255, g: 255, b: 255, a: 1.0};

    var fontColor = parameters.hasOwnProperty("color") ?
      parameters["color"] : "#000000";

    //var spriteAlignment = parameters.hasOwnProperty("alignment") ?
    //	parameters["alignment"] : THREE.SpriteAlignment.topLeft;

    var spriteAlignment = new THREE.Vector2(0, 0);


    var canvas = document.createElement('canvas');
    // canvas.height=128;
    // canvas.width=256;
    var context = canvas.getContext('2d');
    // context.font = "Bold " + fontsize + "px " + fontface;
    context.font = "42px " + fontface;
    // get size data (height depends only on font size)
    var metrics = context.measureText(message);
//
//        // background color
//        context.fillStyle   = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
//            + backgroundColor.b + "," + backgroundColor.a + ")";
//        // border color
    context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
      + borderColor.b + "," + borderColor.a + ")";
//
//        context.lineWidth = borderThickness;
//        context.strokeRect(borderThickness/2, borderThickness/2, metrics.width + borderThickness, fontsize * 1.4 + borderThickness);

    // text color
    context.fillStyle = fontColor;

    context.fillText(message, borderThickness, fontsize + borderThickness);

    // canvas contents will be used for a texture
    var texture = new THREE.Texture(canvas)
    texture.needsUpdate = true;


    var spriteMaterial = new THREE.SpriteMaterial(
      {map: texture});
    var sprite = new THREE.Sprite(spriteMaterial);
    sprite.scale.set(100, 50, 1.0);
    sprite.width = metrics.width;
    sprite.height = 30 * 1.5;

    return sprite;
  }

  //resize the map
  this.resize = function (width, height) {
    _this.camera.aspect = width / height;
    _this.camera.updateProjectionMatrix();

    _this.renderer.setSize(width, height);
    _controls.viewChanged = true;
  }

  _this.init();
  animate();

}
